#lang racket

;; problem set 8

(require rackunit)
(require rackunit/text-ui)
(require 2htdp/universe)   
(require 2htdp/image)

(provide World<%> World% Rectangle<%> Rectangle% make-world run)

;; ALGORITM: 
;; 1. Initially the world has only the target which is placed at the 
;;    center of the canvas.
;;
;; 2. On pressing "n" a new rectangle is created with its center located at
;;    the center of the target and starts moving right at a constant speed.
;;
;; 3. The target and rectangles are draggable within the canvas.
;;
;; Start with (run frame-rate speed).  Typically: (run 0.25 10)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; A NewMouseEvent is a MouseEvent and is one of the following :-
; -- "button-down"
;    Interp: on button down, the object is selected 
; -- "drag"
;    Interp: the object is dragged on the canvas
; -- "button-up"
;    Interp: the object is deselected
; -- "leave"
;    Interp: if the selected object is dragged, then the object is 
;            unselected
; -- ignore all other mouse events
;
; Template:
; mouse-fn : NewMouseEvent -> ??
;(define (mouse-fn mev)
;  (cond [(mouse=? mev "button-down") ...]
;        [(mouse=? mev "drag") ...]
;        [(mouse=? mev "button-up") ...]
;        [(mouse=? mev "leave") ...]
;        [else ...]))

; A NewKeyEvent is a KeyEvent and is one of 
; -- "n"        
;    Interp: when the n key is pressed, a new rectangle is added in the world
;
; -- ignore all other key events
;
; Template:
;key-fn : World NewKeyEvent -> ??
;(define (key-fn w kev) 
;  (cond [(key=? kev "n") ...]
;        [else ...]))  


; ListOf<Rectangle> is a ListOf<Rectangle%> and is either
; - empty
; Interp : no rectangle is present
;
; -(cons Rectangle% ListOf<Rectangle%>)
; Interp : represents a sequence where the first element is the 
; rectangle itself and rest of the elements are represented by 
; ListOf<Rectangle%>
;
; Template :
;(define (lst-rectangle-fn lst-rect)
;  (cond
;    [(empty? lst-rect) ...]
;    [else (...(first lst-rect)
;              (lst-rectangle-fn (rest lst-rect)))]))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; constants used for testing

(define EMPTY-CANVAS (empty-scene 400 500))

;; shape of target
(define OUT-CIRCLE (circle 10 "outline" "red"))

;; height of the target
(define CIRCLE-HALF-HEIGHT (/ (image-height OUT-CIRCLE) 2))

;; width of the target
(define CIRCLE-HALF-WIDTH  (/ (image-width  OUT-CIRCLE) 2))

;; default value of mouse x cordinate
(define POINTER-X 0)

;; default valur of mouse y -cordinate
(define POINTER-Y 0)

;; canvas width 
(define CANVAS-WIDTH 400)

;; canvas height
(define CANVAS-HEIGHT 500)

;; initial value of x cordinate of the center of target 
(define INIT-X (/ CANVAS-WIDTH 2))

;; initial value of y cordinate of the center of target 
(define INIT-Y (/ CANVAS-HEIGHT 2))

;; image of RECTANGLE
(define OUT-RECT (rectangle 30 20 "outline" "green"))

;; height of rectangle
(define RECT-HALF-HEIGHT (/ (image-height OUT-RECT) 2))

;; width of rectangle
(define RECT-HALF-WIDTH  (/ (image-width  OUT-RECT) 2))

;; default positive speed of the rectangle 
;; used for testing
(define GIVEN-SPEED 10)

;; default negative speed of the rectangle 
;; used for testing
(define NEG-SPEED -10)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; INTERFACES :

(define World<%>
  (interface ()
    
    ;; -> World<%>
    ;; Returns the World<%> that should follow this one after a tick
    on-tick                             
    
    ;; Number Number NewMouseEvent -> World<%>
    ;; Returns the World<%> that should follow this one after the
    ;; given MouseEvent
    on-mouse
    
    ;; NewKeyEvent -> World<%>
    ;; Returns the World<%> that should follow this one after the
    ;; given KeyEvent
    on-key
    
    ;; Scene -> Scene
    ;; Returns a Scene like the given one, but with this object drawn
    ;; on it.
    add-to-scene  
    
    ;; -> Number
    ;; Returns the x coordinate of the center of target
    get-x
    
    ;; -> Number
    ;; Returns the y coordinate of the center of target
    get-y
    
    ;; -> Boolean
    ;; Returns true iff the target is selected.
    get-selected?
    
    
    ;; -> ListOf<Rectangle<%>>
    ;; Returns a list of rectangle, present in the World<%>
    get-rectangles
    
    ))

(define Rectangle<%>
  (interface ()
    
    ;; -> Rectangle<%>
    ;; Returns the Rectangle<%> that should follow this one after a tick
    on-tick                             
    
    ;; Number Number NewMouseEvent -> Rectangle<%>
    ;; Returns the Rectangle<%> that should follow this one after the
    ;; given MouseEvent
    on-mouse
    
    ;; NewKeyEvent -> Rectangle<%>
    ;; Returns the Rectangle<%> that should follow this one after the
    ;; given KeyEvent
    on-key
    
    ;; Scene -> Scene
    ;; Returns a Scene like the given one, but with this object drawn
    ;; on it.
    add-to-scene
    
    ;; -> Number
    ;; Returns the x coordinate of the center of rectangle
    get-x
    
    ;; -> Number
    ;; Returns the y coordinate of the center of rectangle
    get-y
    
    ;; -> Boolean
    ;; Returns true iff the rectangle is selected.
    is-selected? ))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; CLASSES

;; A World is a 
;; (new World% [target Target][rectangles ListOf<Rectangle>][speed Number])
;; Interpretation: 
;; represents a world, containing a target, 
;; list of rectangles and an initial speed.
(define World%
   (class* object% (World<%>)
    
    ; represents a Target present in the World 
    (init-field target)
    
    ; represnts a ListOf<Rectangle> that are created.   
    (init-field rectangles)
    
    ; represents the speed at which the rectangles should travel
    (init-field speed)
    
    (super-new) 
    
    ; -> World%
    ; returns a World after a tick event
    ; target stays at its position when not dragged,
    ; and rectangles move by a given speed in a horizontal direction,
    ; initially towards right
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    ; + Higher Order Function composition
    (define/public (on-tick) 
      (new World%
           [target (send target on-tick)] 
           [rectangles (map
                        (lambda (rectangle) (send rectangle on-tick))
                        rectangles)]
           [speed speed]))
    
    ; Number Number NewMouseEvent -> World%
    ; returns a world after a mouse event on the world
    ; Example : See tests below
    ; Strategy :Structural decomposition on this
    ; + Higher Order Function composition
    (define/public (on-mouse x y evt)
      (new World%
           [target (send target on-mouse x y evt)]
           [rectangles (map
                        (lambda (rectangle) (send rectangle on-mouse x y evt))
                        rectangles)]
           [speed speed]))
    
    
    ; NewKeyEvent -> World
    ; given a keyevent returns the world that should folow the given key event
    ; Example : See tests below
    ; Strategy: Structural decomposition on NewKeyEvent (kev)
    (define/public (on-key kev)
      (cond
        [(key=? kev "n")
         (new World%
              [target target]
              [rectangles (cons (create-rect) rectangles)]
              [speed speed])]
        [else this]))
    
    
    ; Scene -> Scene
    ; Given    : a scene,scene0
    ; Where    : scene0 accumulates the context of image of target and 
    ;            rectangle being rendered on canvas.
    ; Produces : a scene that should follow the given scene
    ; Strategy : Structural decomposition on this
    ; + Higher Order function composition + accumulator (scene0)
    (define/public (add-to-scene scene0)
      (local (;Scene -> Scene  
              ;takes a scene and returns a scene after adding the target
              ;onto the scene.
              ;Strategy : Structural decomposition on this
              (define scene-with-target 
                (send target add-to-scene scene0)))
        (foldr
         (lambda (rectangle scene) (send rectangle add-to-scene scene))
         scene-with-target rectangles)))
    
    
    ; -> Rectangle%
    ; creating the Rectangle at the position of the Target
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define (create-rect)
      (new Rectangle% 
           [x (send target get-x)] 
           [y (send target get-y)] 
           [speed speed]
           [pointer-x (send target get-pointer-x)] 
           [pointer-y (send target get-pointer-y)]))
    
    ; -> Target%
    ; returns the target object
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (get-target) 
      target)
    
    ; -> ListOf<Rectangle<%>>
    ; returns a list of rectangle created till now in the world
    ; Example : See tests below
    ; Strategy :Domain Knowledge
    (define/public (get-rectangles) 
      rectangles)
    
    ; -> Number
    ; returns the x-cordinate of the center of target
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (get-x) 
      (send target get-x))
    
    ; -> Number
    ; returns the y-cordinate of the center of target
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (get-y) 
      (send target get-y))
    
    ; -> Boolean
    ; returns true iff the target is selected else returns false
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (get-selected?) 
      (send target get-selected?))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; A Rectangle is a 
;;(new Rectangle% [x Number][y Number]
;;                [speed Number][selected? Boolean] 
;;                [pointer-x Number][pointer-y Number])
(define Rectangle%
  (class* object% (Rectangle<%>) 
    
    (init-field
     ; the rectangle's x position, in pixels, 
     ; relative to the upper-left corner of the canvas
     x  
     ; the rectangle's y position
     ; relative to the upper-left corner of the canvas
     y )         
    
    ; the x-cordinate of the mouse
    (init-field pointer-x) 
    
    ; the y-cordinate of the mouse
    (init-field pointer-y)
    
    ; the speed of the rectangle
    (init-field speed)
    
    ; the rectangle's selected? status
    ; initially false.
    (init-field [selected? false])   
    
    ; image of the rectangle
    (field [IMG (rectangle 30 20 "outline" "green")])     
    
    (super-new)
    
    ; -> Rectangle%
    ; returns a rectnagle after a tick event
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (on-tick)
      (local (;Number -> Rectangle
              ;returns a rectangle after the value of x-ordinate is provided
              ;Strategy : Function Composition
              (define (create-rect new-x)
                (new Rectangle% [x new-x][y y][speed speed]
                     [selected? selected?][pointer-x pointer-x]
                     [pointer-y pointer-y])))
        
        (cond
          [selected? (create-rect x)]
          [(send this touching-right-edge?)
           (send this update-at-right-edge)]
          [(send this crossing-right-edge?)
           (send this update-after-right-edge)]
          [(send this is-rect-at-left-edge?)
           (send this new-pos-at-left-edge)]
          [(send this is-rect-crossing-left-edge?) 
           (send this new-pos-after-left-edge)]
          [else (create-rect (+ x speed))])))
    
    
    
    ; -> Boolean
    ; returns true iff the given rectangle is touching the right edge of
    ; canvas else false.
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (touching-right-edge?)
      (= x (- CANVAS-WIDTH RECT-HALF-WIDTH)))
    
    ; -> Rectangle%
    ; returns a rectangle after a 
    ; tick event if the rectangle is touching right edge of the canvas
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (update-at-right-edge)
      (new Rectangle% 
           [x (- x (send this right-direction))]
           [y y] 
           [speed (send this left-direction)]
           [selected? selected?][pointer-x pointer-x][pointer-y pointer-y]))
    
    ; -> Number
    ; returns the same value if speed is positive else returns 
    ; a positive value of speed
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (right-direction)
      (if (positive? speed) speed (- speed)))
    
    ; -> Number
    ; returns the same value if speed is negative else returns 
    ; a negative value of given speed
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (left-direction)
      (if (negative? speed) speed (- speed)))
    
    ; -> Boolean
    ; returns true iff te rectangle will cross the right edge after 
    ; the tick event
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (crossing-right-edge?)
      (and (positive? speed)
           (> (+ x speed) (- CANVAS-WIDTH RECT-HALF-WIDTH))))
    
    ; -> Rectangle%
    ; returns a rectangle after tick, when the rectangle will
    ; cross the right edge on next tick
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (update-after-right-edge)
      (new Rectangle% [x (- CANVAS-WIDTH RECT-HALF-WIDTH)] [y y]
           [speed (send this left-direction)]
           [selected? selected?]
           [pointer-x pointer-x] [pointer-y pointer-y]))
    
    ; -> Boolean
    ; returns true iff the rectangle is at left edge
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (is-rect-at-left-edge? )
      (= x RECT-HALF-WIDTH))
    
    ; -> Rectangle%
    ; returns a rectangle after tick, when the rectangle will touch the 
    ; left edge on next tick
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (new-pos-at-left-edge)
      (new Rectangle% 
           [x (+ x  (send this right-direction))]
           [y y]
           [speed (send this right-direction)]
           [selected? selected?]
           [pointer-x pointer-x] [pointer-y pointer-y]))
    
    ; -> Boolean
    ; returns true iff the rectangle will cross the left edge 
    ; of the canvas on next tick event
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (is-rect-crossing-left-edge?)
      (and (negative? speed)
           (< (- x (- speed)) RECT-HALF-WIDTH)))
    
    ; -> Rectangle%
    ; returns a rectangle, starts moving right after touching the left edge
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (new-pos-after-left-edge)
      (new Rectangle% 
           [x RECT-HALF-WIDTH] [y y]
           [speed (right-direction)] [selected? selected?]
           [pointer-x pointer-x] [pointer-y pointer-y]))
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    ; NewKeyEvent -> Rectangle%
    ; a rectangle ignores key event and returns the same rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (on-key kev)
      this)      
    
    ; Number Number NewMouseEvent -> World%
    ; returns the rectangle that should follow this one after the given
    ; mouse event.
    ; Example : See tests below
    ; Strategy : Structural decomposition on evt : [NewMouseEvent]
    (define/public (on-mouse mouse-x mouse-y evt)
      (cond 
        [(mouse=? evt "button-down")
         (send this rect-after-button-down mouse-x mouse-y)]
        [(mouse=? evt "drag") 
         (send this set-new-pos-on-drag mouse-x mouse-y)]
        [(mouse=? evt "button-up")
         (send this rect-after-button-up)]
        [(mouse=? evt "leave") (send this unpause-rect)]
        [else this]))
    
    
    ; -> Rectangle%
    ; returns a rectangle that is unselected
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (unpause-rect)
      (new Rectangle% [x x][y y][speed speed]
           [selected? false] [pointer-x pointer-x] [pointer-y pointer-y]))
    
    
    ; -> Rectangle%
    ; returns the rectangle after a button down.  If the event is inside
    ; the rectangle, return a rectangle just like this rectangle, 
    ; except that it is selected.  Otherwise return the rectangle unchanged.
    ; Example : See tests below
    ; Strategy : Function Composition
    (define/public (rect-after-button-down mouse-x mouse-y)
      (if (send this pnt-in-rect? mouse-x mouse-y)
          (new Rectangle% [x x][y y][speed speed][selected? true]
               [pointer-x mouse-x] [pointer-y mouse-y]) 
          this))
    
    
    ; Number Number -> Rectangle%
    ; takes the cordinates of mouse, and returns a rectangle after
    ; it has been dragged relative to the mouse
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (set-new-pos-on-drag mx my)
      (if 
       selected?
       (new Rectangle% 
            [x (send this new-pos-x-right-left-edge mx)] 
            [y (send this new-pos-y-top-bottom-edge my)] 
            [speed speed] [selected? true]
            [pointer-x mx]  [pointer-y my])
       this))
    
    ; Number -> Number 
    ; takes the x-cordinate of the mouse and returns the x-cordinate 
    ; of the rectangle
    ; Example : See tests below
    ; Strategy : Function Composition
    (define/public (new-pos-x-right-left-edge mx)
      (cond 
        [(>= (+ mx RECT-HALF-WIDTH) CANVAS-WIDTH) 
         (- CANVAS-WIDTH RECT-HALF-WIDTH)]
        [(<= (- mx RECT-HALF-WIDTH) 0) RECT-HALF-WIDTH]
        [else (send this new-pos-x-right-left-edge-helper mx)]))
    
    ; Number -> Number
    ; takes the x-cordinate of the mouse and returns the x-cordinate 
    ; of the rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (new-pos-x-right-left-edge-helper mx)
      (- mx (- pointer-x x)))
    
    ; Number -> Number
    ; takes the y-cordinate of the mouse and returns the y-cordinate 
    ; of the rectangle
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (new-pos-y-top-bottom-edge my)
      (cond 
        [ (<= (- my RECT-HALF-WIDTH) 0) RECT-HALF-WIDTH]
        [ (>= (+ my RECT-HALF-WIDTH) CANVAS-HEIGHT)
          (- CANVAS-HEIGHT RECT-HALF-WIDTH)]
        [else (send this new-pos-y-top-bottom-edge-helper my)]))
    
    ; Number -> Number
    ; takes the y-cordinate of the mouse and returns the y-cordinate 
    ; of the rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (new-pos-y-top-bottom-edge-helper my)
      (- my (- pointer-y y)))
    
    ; -> Rectangle%
    ; button-up unselects the rectangle
    ; Example : See tests below
    ; Strategy : Function composition
    (define/public (rect-after-button-up)
      (new Rectangle% [x x][y y][speed speed][selected? false] 
           [pointer-x pointer-x] [pointer-y pointer-x]))
    
    ; Scene -> Scene
    ; takes a scene and returns a scene after placing the image 
    ; of rectangle onto the given scene
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (add-to-scene scene)
      (place-image IMG x y scene))
    
    ; -> Boolean
    ; returns true iff the mouse co-ordinates are inside the rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (pnt-in-rect? pointer-x pointer-y )
      (and
       (< (- x RECT-HALF-WIDTH) pointer-x (+ x RECT-HALF-WIDTH))
       (< (- y RECT-HALF-HEIGHT) pointer-y (+ y RECT-HALF-HEIGHT))))
    
    
    ;; getters used for testing:
    ; -> Number
    ; returns the x-cordinate of the center of rectangle
    ; Example : See tests below
    ; Strategy :Domain Knowledge
    (define/public (get-x) x)
    
    ; -> Number
    ; returns the y-cordinate of the center of rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (get-y) y)
    
    ; -> Number
    ; returns the speed of the rectangle
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (get-speed) speed)
    
    ; -> Boolean
    ; returns true iff the rectangle s selected, else false
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (is-selected?) selected?)
    
    ; -> Number
    ; returns the x-cordinate of the mouse when rectangle is selected
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (get-pointer-x) pointer-x)
    
    ; -> Number
    ; returns the y-cordinate of the mouse when rectangle is selected
    ; Example : See tests below
    ; Strategy :Domain Knowledge
    (define/public (get-pointer-y) pointer-y)
        
    ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; A Target is a 
;;(new Target% [x Number][y Number]
;;             [selected? Boolean]
;;             [pointer-x Number][pointer-y Number])
(define Target%
  (class* object% ()
    (init-field
     x ; the x position of center of target
     y ; the y position of center of target
     )
    
    ; the field pointer-x represents the value of x-cordinate of mouse
    ; when the target is selected
    (init-field pointer-x)
    
    ; the field pointer-y represents the value of y-cordinate of mouse
    ; when the target is selected
    (init-field pointer-y)
    
    ; the field selected? represents if the target is selected or not
    (init-field selected?)
    
    ; image of target
    ; reresents the image of the target
    (field [TARGET-IMG (circle 10 "outline" "red")])
    
    (super-new)
    
    ; -> Target%
    ; returns the target after a tick event
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (on-tick)
      this)
    
    ; Number Number NewMouseEvent -> Target%
    ; takes the x and y cordinates of the mouse and the key event
    ; and returns the target after the mouse event.
    ; Example : See tests below
    ; Strategy : Structural decomposition on evt :[NewMouseEvent]
    (define/public (on-mouse mouse-x mouse-y evt)
      (cond
        [(mouse=? evt "button-down")
         (send this target-after-button-down mouse-x mouse-y)]
        [(mouse=? evt "drag") 
         (send this set-new-pos-on-drag mouse-x mouse-y)]
        [(mouse=? evt "button-up")
         (send this target-after-button-up)]
        [else this]))
    
    ; Number Number -> Target%
    ; returns the target after a button down event. If the event is inside
    ; the target, returns a target that it is selected.  
    ; Otherwise returns the same unselected target.
    ; Example : See tests below
    ; Startegy : Function Composition
    (define/public (target-after-button-down mouse-x mouse-y)
      (if (send this in-target? mouse-x mouse-y)
          (new Target% [x x][y y][selected? true]
               [pointer-x mouse-x] [pointer-y mouse-y])
          this))
    
    ; Number Number -> Target%
    ; takes the mouse x and y co-ordinates and returns the target
    ; moved relative to the mouse.
    ; Example : See tests below
    ; Strategy : Structural decomposition on this
    (define/public (set-new-pos-on-drag mx my)
      (if selected?
       (new Target% 
            [x (send this new-pos-x-right-left-edge-target mx)]
            [y (send this new-pos-y-top-bottom-edge-target my)] 
            [selected? true] 
            [pointer-x mx]  [pointer-y my])
       this))
    
    ; Number -> Number
    ; takes the x-cordinate of the mouse and returns the x-cordinate of the
    ; target after the target is moved relatively.
    ; Example : See tests below
    ; Startegy : Function Composition
    (define/public (new-pos-x-right-left-edge-target mx)
      (cond  
        [(>= (+ mx CIRCLE-HALF-WIDTH) CANVAS-WIDTH) 
         (- CANVAS-WIDTH CIRCLE-HALF-WIDTH)]
        [(<= (- mx CIRCLE-HALF-WIDTH) 0) CIRCLE-HALF-WIDTH]
        [else (send this new-pos-x-right-left-edge-target-helper mx)]))
    
    ; Number -> Number
    ; takes the x-cordinate of the mouse and returns the x-cordinate of the
    ; target after the target is moved relatively.
    ; Example : See tests below
    ; Startegy : Domain Knowledge 
    (define/public (new-pos-x-right-left-edge-target-helper mx)
      (- mx (- pointer-x x)))
    
    ; Number -> Number
    ; takes the y-cordinate of the mosue and returns the y-cordinate of the
    ; target after the target has been moved relatively.
    ; Example : See tests below
    ; Startegy : Function Composition
    (define/public (new-pos-y-top-bottom-edge-target my)
      (cond [ (<= (- my CIRCLE-HALF-WIDTH) 0) CIRCLE-HALF-WIDTH]
            [ (>= (+ my CIRCLE-HALF-WIDTH) CANVAS-HEIGHT)
              (- CANVAS-HEIGHT CIRCLE-HALF-WIDTH)]
            [else (new-pos-y-top-bottom-edge-target-helper my)]))
    
    ; Number -> Number
    ; takes the x-cordinate of the mosue and returns the y-cordinate of the
    ; target after the target has been moved relatively.
    ; Example : See tests below
    ; Startegy : Domain Knowledge 
    (define/public (new-pos-y-top-bottom-edge-target-helper my)
      (- my (-  pointer-y y)))
    
    
    ; -> Target%
    ; Returns a target after the button-up event is applied
    ; Example : See tests below
    ; Strategy : Function Composition
    (define/public (target-after-button-up)
      (new Target% [x x][y y][selected? false] [pointer-x pointer-x] 
           [pointer-y pointer-y]))
    
    ; Number Number -> Boolean
    ; takes the x and y cordinates of the mouse and returns true iff the
    ; mouse cordinates are within the target, else false
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (in-target? mx my)
      (and 
       (< (- x CIRCLE-HALF-WIDTH) mx (+ x CIRCLE-HALF-WIDTH))
       (< (- y CIRCLE-HALF-WIDTH) my (+ y CIRCLE-HALF-WIDTH))))
    
    
    ; NewKeyEvent -> Target%
    ; Returns the same target ignoring the keyevent
    ; Example : See tests below
    ; Strategy : Domain Knowledge
    (define/public (on-key kev)
      this)    
    
    ; Scene -> Scene
    ; takes a scene and returns a new scene after placing the image of target
    ; Example : See tests below
    ; Strategy : Domain knowledge 
    (define/public (add-to-scene scene)
      (place-image TARGET-IMG x y scene))
    
    ; getters: used only for testing
    ; -> Number
    ; returns the x cordinate of the center of target 
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-x) x)
    
    ; -> Number
    ; returns the y cordinate of the center of target 
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-y) y)
    
    ; -> Boolean
    ; returns true if the target is selected else false
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-selected?) selected?)
    
    ; -> Number
    ; returns the x cordinate of the mouse when target is selected
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-pointer-x) pointer-x)
    
    ; -> Number
    ; returns the y cordinate of the mouse when target is selected
    ; Example : See tests below
    ; Strategy : Domain knowledge
    (define/public (get-pointer-y) pointer-y)
    ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; run : Number Number -> World%
;; takes in the frame rate (secs/tick), and speed ,determines the speed
;; with which rectangles move horizontally,initially towards right and 
;; returns a world
;; which has a target placed at the center of the canvas and any rectangle
;; added to the world.
;; Example : 
;; (run .25 10) -> produces  a world which has a target placed at the center
;; with no rectangle.
;; INVARIANT : the given speed is > 0
;; Strategy : Function Composition
(define (run frame-rate given-speed)
  (big-bang (make-world given-speed)
            (on-tick (lambda (w) (send w on-tick)) frame-rate)
            (on-draw (lambda (w) (send w add-to-scene EMPTY-CANVAS)))
            (on-key (lambda (w kev) (send w on-key kev)))
            (on-mouse (lambda (w x y evt) (send w on-mouse x y evt)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; make-world : Number -> World%
;; takes in a the speed with which the rectangles move inside the world
;; and returns a world, where rectangles have not been added, but any
;; rectangles added in future will move with the given speed, horizontally,
;; and initially towards right.
;; Example : See tests below
;; Strategy : Function Composition
(define (make-world given-speed)
  (new World% 
       [target (new Target% [x INIT-X][y INIT-Y]
                    [selected? false][pointer-x POINTER-X]
                    [pointer-y POINTER-Y])]
       [rectangles empty]
       [speed given-speed]))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;  

;; Test Cases

(define new-target
  (new Target% [x 100][y 100][selected? true]
       [pointer-x POINTER-X][pointer-y POINTER-Y]))

;; Tests for Target and world
(define-test-suite world-target-tests
  (local ((define w1 (send (make-world GIVEN-SPEED) on-key "d"))
          (define w2 (send w1 on-key "d")))
    (check-equal?
     (length (send w2 get-rectangles))
     0
     "Check for other key event for creation of rectangle"))
  
  (local ((define w1 (send (make-world GIVEN-SPEED) 
                           on-mouse INIT-X INIT-Y "button-down")))
    (check-equal?
     (send w1 get-selected?)
     true
     "Check for button-down event, clicked inside the target")
    (check-equal?
     (send w1 get-x)
     INIT-X
     "Check if the X coordinate of the target is same as the position 
      it was created")
    (check-equal?
     (send w1 get-y)
     INIT-Y
     "Check if the Y coordinate of the target is same as the position 
      it was created"))
  
  (local ((define w1 (send (make-world GIVEN-SPEED) 
                           on-mouse INIT-X INIT-Y "button-up")))
    (check-equal?
     (send w1 get-selected?)
     false
     "Check for button-up event, when target is unselected"))
  
  (local ((define w1 (send (make-world GIVEN-SPEED) 
                           on-mouse INIT-X INIT-Y "move")))
    (check-equal?
     (send w1 get-x)
     INIT-X
     "Check for other mouse event, on target created"))
  
  (local ((define w0 (make-world GIVEN-SPEED))
          (define w1 (send (make-world GIVEN-SPEED) on-mouse INIT-X INIT-Y
                           "button-down"))
          (define w2 (send w1 on-mouse (+ INIT-X 8) INIT-Y "drag"))
          (define w3 (send (make-world GIVEN-SPEED) 
                           on-mouse (+ INIT-X 8) INIT-Y "button-up"))
          (define w4 (send w3 on-mouse (+ INIT-X 50) INIT-Y "button-down"))
          (define w5 (send (make-world GIVEN-SPEED) on-mouse 
                           (+ INIT-X 50) INIT-Y "drag"))
          (define w6 (send w1 on-mouse (- CANVAS-WIDTH CIRCLE-HALF-WIDTH) 
                           INIT-Y "drag"))
          (define w7 (send w6 on-mouse 
                           (+ 10 (- CANVAS-WIDTH CIRCLE-HALF-WIDTH))
                           INIT-Y "drag"))
          (define w8 (send w6 on-mouse 
                           (- CIRCLE-HALF-WIDTH) INIT-Y "drag"))
          (define w9 (send w6 on-mouse INIT-X (- INIT-Y) "drag"))
          (define w10(send w6 on-mouse 
                           INIT-X (+ CANVAS-HEIGHT INIT-Y) "drag"))
          (define w11 (send new-target on-key "i"))
          (define w12 (send (make-world GIVEN-SPEED) on-tick))
          (define w13 (send (make-world GIVEN-SPEED) get-target))
          (define w15 (send w0 on-key "n"))
          (define w16 (send w15 add-to-scene EMPTY-CANVAS))
          (define w17 (send w15 on-tick))
          (define w18 (send w15 on-mouse INIT-X INIT-Y "button-down"))
          )
    
    (check-equal?
     (send w2 get-selected?)
     #t
     "Check for button-down, after target is dragged")
    (check-equal?
     (send w2 get-x)
     (+ INIT-X 8)
     "Check for new X coordinate, after target is dragged")
    (check-equal?
     (send w4 get-selected?)
     #f
     "Check for button-down, where the click is outside the target")
    (check-equal?
     (send w5 get-selected?)
     #f
     "Check for drag event, where the click is outside the target")
    (check-equal?
     (send w6 get-x)
     (send w7 get-x)
     "Check for the X coordinate, when target is clicked and dragged 
      outside the canvas towards the right")
    (check-equal?
     (send w8 get-x)
     CIRCLE-HALF-WIDTH
     "Check for the X coordinate, when target is clicked and dragged 
      outside the canvas towards the left")
    (check-equal?
     (send w9 get-y)
     10
     "Check for the Y coordinate, when target is clicked and dragged 
      outside the canvas towards the top")
    (check-equal?
     (send w10 get-y)
     (- CANVAS-HEIGHT CIRCLE-HALF-WIDTH)
     "Check for the Y coordinate, when target is clicked and dragged 
      outside the canvas towards the bottom")
    
    (check-equal?
     (send (make-world 2) add-to-scene EMPTY-CANVAS)
     (place-image (circle 10 "outline" "red") INIT-X INIT-Y EMPTY-CANVAS)
     "Check for add-to-scene, by adding target image on an empty canvas")
    (check-equal?
     (send w11 get-pointer-x)
     0
     "Check for pointer-x coordinate of target when it is not selected")
    (check-equal?
     (send w11 get-pointer-y)
     0
     "Check for pointer-x coordinate of target when it is not selected")))

(run-tests world-target-tests)

;; ***************************tests for rectangle ***************************
(define a-new-rectangle 
  (new Rectangle% [x 100][y 100][speed  GIVEN-SPEED]
       [selected? true] [pointer-x POINTER-X] [pointer-y POINTER-Y]))

(define another-rectangle-with-negative-speed
  (new Rectangle% [x 385][y 100][speed NEG-SPEED]
       [selected? false][pointer-x POINTER-X][pointer-y POINTER-Y]))

(define rectangle-to-cross-right 
  (new Rectangle% [x 380][y 100][speed GIVEN-SPEED]
       [selected? false] [pointer-x POINTER-X] [pointer-y POINTER-Y]))

(define rectangle-to-cross-left
  (new Rectangle% [x 20][y 100][speed NEG-SPEED]
       [selected? false] [pointer-x POINTER-X] [pointer-y POINTER-Y]))

(define rectangle-moving-left
  (new Rectangle% [x 100][y 100][speed NEG-SPEED]
       [selected? false] [pointer-x POINTER-X] [pointer-y POINTER-Y]))

(define-test-suite world-rect-tests
  (local 
    ((define w1 (send a-new-rectangle on-mouse 0 100 "drag"))
     (define w2 (send w1 on-mouse 200 100 "button-up"))
     (define w3 (send w2 on-tick)) 
     (define w5 (send a-new-rectangle on-mouse CANVAS-WIDTH 100 "drag"))
     (define w6 (send w5 on-mouse 200 100 "button-up"))
     (define w7 (send w6 on-tick))
     (define w8 (send rectangle-to-cross-right on-tick))
     (define w9 (send rectangle-to-cross-left on-tick))
     (define w10 (send a-new-rectangle on-mouse 200 100 "button-up"))
     (define w11 (send rectangle-moving-left on-tick))
     (define w12 (send rectangle-moving-left on-mouse 100 100 "button-down"))
     (define w13 (send a-new-rectangle on-mouse 600 100 "drag"))
     (define w13a (send w13 on-mouse 500 100 "leave"))
     (define w14 (send a-new-rectangle on-mouse 600 100 "move"))
     (define w15 (send a-new-rectangle on-key "n"))
     (define w16 (send rectangle-moving-left on-mouse 300 300 "button-down"))
     (define w17 
       (send rectangle-moving-left on-mouse CANVAS-WIDTH 100 "drag"))
     (define w18 (send a-new-rectangle on-mouse 130 100 "drag"))
     (define w19 (send a-new-rectangle on-mouse 130 CANVAS-HEIGHT "drag"))
     (define w20 (send a-new-rectangle on-mouse 130 -100 "drag"))
     (define w21 (send another-rectangle-with-negative-speed on-tick)))
    
    (check-equal?
     (send w6 get-x)
     385
     "Check for X coordinate of the rect at the right end of canvas after 
    dragged from initial position")
    (check-equal? 
     (send w7 get-x) 
     375
     "Check for X coordinate of the rect after a tick, after dragged from 
    initial position to the right end of canvas")
    (check-equal?
     (send w3 get-x)
     25
     "Check for X coordinate of the rect after a tick, after dragged from 
    initial position to the left end of canvas")
    (check-equal?
     (send w8 get-x)
     385
     "Check for X coordinate of the rect after a tick, after reaching 
    the right end of canvas")
    (check-equal?
     (send w9 get-x)
     15
     "Check for X coordinate of the rect after a tick, after reaching 
    the left end of canvas")
    (check-equal?
     (send (send a-new-rectangle on-tick) get-x)
     100
     "Check for X coordinate of rectangle after a tick, 
    when rectangle is being selected") 
    (check-equal?
     (send (send w10 on-tick) get-x)
     110
     "Check for X coordinate of rectangle after a tick, 
    when rectangle is not being selected")
    (check-equal?
     (send w11 get-x)
     90
     "Check for X coordinate of rectangle after a tick, 
    when rectangle is moving left")
    (check-equal?
     (send w12 is-selected?)
     #t
     "Check for button-down event on rectangle, 
      when rectangle is moving left")
    (check-equal?
     (send w13a is-selected?)
     #f
     "Check for mouse leave event after rectangle is selected 
     and dragged outside the canvas")
    (check-equal?
     (send w14 is-selected?)
     (send a-new-rectangle is-selected?)
     "Check for other mouse event on the rectangle")
    (check-equal?
     (send w15 get-x) 
     (send a-new-rectangle get-x)
     "Check for key event on the rectangle")
    (check-equal?
     (send w16 is-selected?) 
     #f
     "Check for button-down event by clicking outside the rectangle")
    (check-equal?
     (send w17 is-selected?) 
     #f
     "Check for drag event by dragging an unselected rectangle")
    (check-equal?
     (send w17 get-pointer-x)
     0
     "Check for pointer-x coordinate of rectangle when it is not selected")
    (check-equal?
     (send w17 get-pointer-y) 
     0
     "Check for pointer-y coordinate of rectangle when it is not selected")
    (check-equal?
     (send w21 get-speed)
     -10
     "Check for speed of the rectangle when it is moving towards left")
    (check-equal?
     (send w21 get-y)
     100
     "Check for Y coordinate of rectangle when it is moving towards left")))

(run-tests world-rect-tests)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;